library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.processor_types.all;

entity alu is
	port (
		clock: in std_ulogic;
        reset : in std_ulogic;
        alu_control: in alu_bus;
        in1 : in bit32;
        in2 : in bit32;
		result : out bit32
	);
end alu;

architecture arch_alu of alu is
    signal pir, ir, slt : bit32;
	signal add_res2 : signed(32 downto 0);
	signal add_res : signed(33 downto 0);
    signal mult_res, mult_res_clked : signed(64 downto 0);
	signal in1_o, in2_o : signed(33 downto 0);
	signal ls1, ls2, ls4, ls8, ls16 : std_logic_vector(31 downto 0);
begin
	-- psl property check_mult is
	--   always (alu_control(subtract) -> (alu_control(shiftright) or alu_control(setltu))) @rising_edge(clock);
	-- psl assert check_mult;
	
	-- psl property psl_init is
	--   always ({alu_control(init_mult)} |=> {not alu_control(init_mult); alu_control(shiftright)[*32]}) @rising_edge(clock);
	-- psl assert psl_init;
	
    ir <=   std_logic_vector(mult_res(63 downto 32))   when alu_control(shiftright)='1'    else
			std_logic_vector(add_res(32 downto 1))   when alu_control(addition)='1'    else
            std_logic_vector(signed(in1) nor signed(in2)) when alu_control(not_or)='1'  else
            slt                                           when alu_control(setltu)='1'   else
            ls16										  when alu_control(shiftleft)='1'  else
            std_logic_vector(mult_res(63 downto 32)) when alu_control(gethigh)='1' else
            std_logic_vector(mult_res(31 downto 0)) when alu_control(getlow)='1' else
            (others=>'0');
    
    slt <=  (0 => add_res(33), others => '0');
    
	in1_o <= mult_res_clked(64 downto 32) & '1' when alu_control(shiftright)='1' else
			 signed(in1(31) & in1 & '1');
		 
	in2_o <= signed((not (in2(31) & in2)) & '1') when alu_control(subtract)='1' else
			 signed(in2(31) & in2 & '0');
			 
	add_res <= in1_o + in2_o;
	add_res2 <= add_res(33 downto 1);

	mult_res <= (others => '0') when alu_control(init_mult)='1' else
				add_res2(32) & add_res2 & mult_res_clked(31 downto 1)	when alu_control(shiftright)='1' else
				mult_res_clked;
	
    result <= pir;
	
	-- left shifters
	ls1 <= in1(30 downto 0) & '0' when in2(0)='1' else in1;
	ls2 <= ls1(29 downto 0) & "00" when in2(1)='1' else ls1;
	ls4 <= ls2(27 downto 0) & "0000" when in2(2)='1' else ls2;
	ls8 <= ls4(23 downto 0) & "00000000" when in2(3)='1' else ls4;
	ls16 <= ls8(15 downto 0) & "0000000000000000" when in2(4)='1' else ls8;
	
	seq: process(clock, reset)
	begin
        if (reset = '1') then
            pir <= (others => '0');
			mult_res_clked <= (others => '0');
		elsif (rising_edge(clock)) then
			pir <= ir;
			mult_res_clked <= mult_res;
		end if;
	end process;
	
end;
